// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/arguments-inl.h"
#include "src/compiler/wasm-compiler.h"
#include "src/conversions.h"
#include "src/counters.h"
#include "src/debug/debug.h"
#include "src/frame-constants.h"
#include "src/heap/factory.h"
#include "src/message-template.h"
#include "src/objects-inl.h"
#include "src/objects/frame-array-inl.h"
#include "src/runtime/runtime-utils.h"
#include "src/trap-handler/trap-handler.h"
#include "src/v8memory.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-value.h"

namespace v8 {
namespace internal {

    namespace {

        WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate)
        {
            StackFrameIterator it(isolate, isolate->thread_local_top());
            // On top: C entry stub.
            DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
            it.Advance();
            // Next: the wasm compiled frame.
            DCHECK(it.frame()->is_wasm_compiled());
            WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
            return frame->wasm_instance();
        }

        Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate)
        {
            return GetWasmInstanceOnStackTop(isolate)->native_context();
        }

        class ClearThreadInWasmScope {
        public:
            ClearThreadInWasmScope()
            {
                DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(),
                    trap_handler::IsThreadInWasm());
                trap_handler::ClearThreadInWasm();
            }
            ~ClearThreadInWasmScope()
            {
                DCHECK(!trap_handler::IsThreadInWasm());
                trap_handler::SetThreadInWasm();
            }
        };

        Object ThrowWasmError(Isolate* isolate, MessageTemplate message)
        {
            HandleScope scope(isolate);
            Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(message);
            return isolate->Throw(*error_obj);
        }
    } // namespace

    RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(1, args.length());
        CONVERT_ARG_HANDLE_CHECKED(Object, function, 0);

        if (function->IsNull(isolate)) {
            return Smi::FromInt(true);
        }
        if (WasmExportedFunction::IsWasmExportedFunction(*function)) {
            return Smi::FromInt(true);
        }
        return Smi::FromInt(false);
    }

    RUNTIME_FUNCTION(Runtime_WasmMemoryGrow)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(2, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
        // which calls this runtime function.
        CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);

        // This runtime function is always being called from wasm code.
        ClearThreadInWasmScope flag_scope;

        int ret = WasmMemoryObject::Grow(
            isolate, handle(instance->memory_object(), isolate), delta_pages);
        // The WasmMemoryGrow builtin which calls this runtime function expects us to
        // always return a Smi.
        return Smi::FromInt(ret);
    }

    RUNTIME_FUNCTION(Runtime_ThrowWasmError)
    {
        ClearThreadInWasmScope clear_wasm_flag;
        DCHECK_EQ(1, args.length());
        CONVERT_SMI_ARG_CHECKED(message_id, 0);
        return ThrowWasmError(isolate, MessageTemplateFromInt(message_id));
    }

    RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow)
    {
        SealHandleScope shs(isolate);
        DCHECK_LE(0, args.length());
        return isolate->StackOverflow();
    }

    RUNTIME_FUNCTION(Runtime_WasmThrowTypeError)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(0, args.length());
        THROW_NEW_ERROR_RETURN_FAILURE(
            isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
    }

    RUNTIME_FUNCTION(Runtime_WasmThrowCreate)
    {
        // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
        HandleScope scope(isolate);
        DCHECK_EQ(2, args.length());
        DCHECK(isolate->context().is_null());
        isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
        CONVERT_ARG_CHECKED(WasmExceptionTag, tag_raw, 0);
        CONVERT_SMI_ARG_CHECKED(size, 1);
        // TODO(mstarzinger): Manually box because parameters are not visited yet.
        Handle<Object> tag(tag_raw, isolate);
        Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
            MessageTemplate::kWasmExceptionError);
        CHECK(!Object::SetProperty(isolate, exception,
            isolate->factory()->wasm_exception_tag_symbol(),
            tag, StoreOrigin::kMaybeKeyed,
            Just(ShouldThrow::kThrowOnError))
                   .is_null());
        Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
        CHECK(!Object::SetProperty(isolate, exception,
            isolate->factory()->wasm_exception_values_symbol(),
            values, StoreOrigin::kMaybeKeyed,
            Just(ShouldThrow::kThrowOnError))
                   .is_null());
        return *exception;
    }

    RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag)
    {
        // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
        HandleScope scope(isolate);
        DCHECK_EQ(1, args.length());
        DCHECK(isolate->context().is_null());
        isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
        CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
        // TODO(mstarzinger): Manually box because parameters are not visited yet.
        Handle<Object> except_obj(except_obj_raw, isolate);
        return *WasmExceptionPackage::GetExceptionTag(isolate, except_obj);
    }

    RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues)
    {
        // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
        HandleScope scope(isolate);
        DCHECK_EQ(1, args.length());
        DCHECK(isolate->context().is_null());
        isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
        CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
        // TODO(mstarzinger): Manually box because parameters are not visited yet.
        Handle<Object> except_obj(except_obj_raw, isolate);
        return *WasmExceptionPackage::GetExceptionValues(isolate, except_obj);
    }

    RUNTIME_FUNCTION(Runtime_WasmRunInterpreter)
    {
        DCHECK_EQ(2, args.length());
        HandleScope scope(isolate);
        CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
        CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);

        // The arg buffer is the raw pointer to the caller's stack. It looks like a
        // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
        // cast it back to the raw pointer.
        CHECK(!arg_buffer_obj->IsHeapObject());
        CHECK(arg_buffer_obj->IsSmi());
        Address arg_buffer = arg_buffer_obj->ptr();

        ClearThreadInWasmScope wasm_flag;

        // Find the frame pointer and instance of the interpreter frame on the stack.
        Handle<WasmInstanceObject> instance;
        Address frame_pointer = 0;
        {
            StackFrameIterator it(isolate, isolate->thread_local_top());
            // On top: C entry stub.
            DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
            it.Advance();
            // Next: the wasm interpreter entry.
            DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
            instance = handle(
                WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate);
            frame_pointer = it.frame()->fp();
        }

        // Reserve buffers for argument and return values.
        DCHECK_GE(instance->module()->functions.size(), func_index);
        wasm::FunctionSig* sig = instance->module()->functions[func_index].sig;
        DCHECK_GE(kMaxInt, sig->parameter_count());
        int num_params = static_cast<int>(sig->parameter_count());
        ScopedVector<wasm::WasmValue> wasm_args(num_params);
        DCHECK_GE(kMaxInt, sig->return_count());
        int num_returns = static_cast<int>(sig->return_count());
        ScopedVector<wasm::WasmValue> wasm_rets(num_returns);

        // Copy the arguments for the {arg_buffer} into a vector of {WasmValue}. This
        // also boxes reference types into handles, which needs to happen before any
        // methods that could trigger a GC are being called.
        Address arg_buf_ptr = arg_buffer;
        for (int i = 0; i < num_params; ++i) {
#define CASE_ARG_TYPE(type, ctype)                                              \
    case wasm::type:                                                            \
        DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetParam(i)),       \
            sizeof(ctype));                                                     \
        wasm_args[i] = wasm::WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \
        arg_buf_ptr += sizeof(ctype);                                           \
        break;
            switch (sig->GetParam(i)) {
                CASE_ARG_TYPE(kWasmI32, uint32_t)
                CASE_ARG_TYPE(kWasmI64, uint64_t)
                CASE_ARG_TYPE(kWasmF32, float)
                CASE_ARG_TYPE(kWasmF64, double)
#undef CASE_ARG_TYPE
            case wasm::kWasmAnyRef:
            case wasm::kWasmAnyFunc:
            case wasm::kWasmExceptRef: {
                DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetParam(i)),
                    kSystemPointerSize);
                Handle<Object> ref(ReadUnalignedValue<Object>(arg_buf_ptr), isolate);
                wasm_args[i] = wasm::WasmValue(ref);
                arg_buf_ptr += kSystemPointerSize;
                break;
            }
            default:
                UNREACHABLE();
            }
        }

        // Set the current isolate's context.
        DCHECK(isolate->context().is_null());
        isolate->set_context(instance->native_context());

        // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
        // nor the {InterpreterHandle} have to exist, because interpretation might
        // have been triggered by another Isolate sharing the same WasmEngine.
        Handle<WasmDebugInfo> debug_info = WasmInstanceObject::GetOrCreateDebugInfo(instance);
        bool success = WasmDebugInfo::RunInterpreter(
            isolate, debug_info, frame_pointer, func_index, wasm_args, wasm_rets);

        // Early return on failure.
        if (!success) {
            DCHECK(isolate->has_pending_exception());
            return ReadOnlyRoots(isolate).exception();
        }

        // Copy return values from the vector of {WasmValue} into {arg_buffer}. This
        // also un-boxes reference types from handles into raw pointers.
        arg_buf_ptr = arg_buffer;
        for (int i = 0; i < num_returns; ++i) {
#define CASE_RET_TYPE(type, ctype)                                         \
    case wasm::type:                                                       \
        DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetReturn(i)), \
            sizeof(ctype));                                                \
        WriteUnalignedValue<ctype>(arg_buf_ptr, wasm_rets[i].to<ctype>()); \
        arg_buf_ptr += sizeof(ctype);                                      \
        break;
            switch (sig->GetReturn(i)) {
                CASE_RET_TYPE(kWasmI32, uint32_t)
                CASE_RET_TYPE(kWasmI64, uint64_t)
                CASE_RET_TYPE(kWasmF32, float)
                CASE_RET_TYPE(kWasmF64, double)
#undef CASE_RET_TYPE
            case wasm::kWasmAnyRef:
            case wasm::kWasmAnyFunc:
            case wasm::kWasmExceptRef: {
                DCHECK_EQ(wasm::ValueTypes::ElementSizeInBytes(sig->GetReturn(i)),
                    kSystemPointerSize);
                WriteUnalignedValue<Object>(arg_buf_ptr, *wasm_rets[i].to_anyref());
                arg_buf_ptr += kSystemPointerSize;
                break;
            }
            default:
                UNREACHABLE();
            }
        }

        return ReadOnlyRoots(isolate).undefined_value();
    }

    RUNTIME_FUNCTION(Runtime_WasmStackGuard)
    {
        SealHandleScope shs(isolate);
        DCHECK_EQ(0, args.length());
        DCHECK(!trap_handler::IsTrapHandlerEnabled() || trap_handler::IsThreadInWasm());

        ClearThreadInWasmScope wasm_flag;

        // Check if this is a real stack overflow.
        StackLimitCheck check(isolate);
        if (check.JsHasOverflowed())
            return isolate->StackOverflow();

        return isolate->stack_guard()->HandleInterrupts();
    }

    RUNTIME_FUNCTION(Runtime_WasmCompileLazy)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(2, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_SMI_ARG_CHECKED(func_index, 1);

        ClearThreadInWasmScope wasm_flag;

#ifdef DEBUG
        StackFrameIterator it(isolate, isolate->thread_local_top());
        // On top: C entry stub.
        DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
        it.Advance();
        // Next: the wasm lazy compile frame.
        DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
        DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
#endif

        auto* native_module = instance->module_object()->native_module();
        wasm::CompileLazy(isolate, native_module, func_index);

        Address entrypoint = native_module->GetCallTargetForFunction(func_index);
        return Object(entrypoint);
    }

    // Should be called from within a handle scope
    Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance,
        Isolate* isolate, uint32_t address)
    {
        DCHECK(instance->has_memory_object());
        Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
            isolate);

        // Validation should have failed if the memory was not shared.
        DCHECK(array_buffer->is_shared());

        // Should have trapped if address was OOB
        DCHECK_LT(address, array_buffer->byte_length());
        return array_buffer;
    }

    RUNTIME_FUNCTION(Runtime_WasmAtomicNotify)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(3, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
        CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
        Handle<JSArrayBuffer> array_buffer = getSharedArrayBuffer(instance, isolate, address);
        return FutexEmulation::Wake(array_buffer, address, count);
    }

    double WaitTimeoutInMs(double timeout_ns)
    {
        return timeout_ns < 0
            ? V8_INFINITY
            : timeout_ns / (base::Time::kNanosecondsPerMicrosecond * base::Time::kMicrosecondsPerMillisecond);
    }

    RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(4, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
        CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
        CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 3);
        double timeout_ms = WaitTimeoutInMs(timeout_ns);
        Handle<JSArrayBuffer> array_buffer = getSharedArrayBuffer(instance, isolate, address);
        return FutexEmulation::Wait32(isolate, array_buffer, address, expected_value,
            timeout_ms);
    }

    RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(5, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
        CONVERT_NUMBER_CHECKED(uint32_t, expected_value_high, Uint32, args[2]);
        CONVERT_NUMBER_CHECKED(uint32_t, expected_value_low, Uint32, args[3]);
        CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 4);
        int64_t expected_value = (static_cast<uint64_t>(expected_value_high) << 32) | static_cast<uint64_t>(expected_value_low);
        double timeout_ms = WaitTimeoutInMs(timeout_ns);
        Handle<JSArrayBuffer> array_buffer = getSharedArrayBuffer(instance, isolate, address);
        return FutexEmulation::Wait64(isolate, array_buffer, address, expected_value,
            timeout_ms);
    }

    namespace {
        Object ThrowTableOutOfBounds(Isolate* isolate,
            Handle<WasmInstanceObject> instance)
        {
            // Handle out-of-bounds access here in the runtime call, rather
            // than having the lower-level layers deal with JS exceptions.
            if (isolate->context().is_null()) {
                isolate->set_context(instance->native_context());
            }
            Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
                MessageTemplate::kWasmTrapTableOutOfBounds);
            return isolate->Throw(*error_obj);
        }
    } // namespace

    RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet)
    {
        // This runtime function is always being called from wasm code.
        ClearThreadInWasmScope flag_scope;

        HandleScope scope(isolate);
        DCHECK_EQ(3, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_UINT32_ARG_CHECKED(table_index, 1);
        CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
        DCHECK_LT(table_index, instance->tables()->length());
        auto table = handle(
            WasmTableObject::cast(instance->tables()->get(table_index)), isolate);

        if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
            return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
        }

        return *WasmTableObject::Get(isolate, table, entry_index);
    }

    RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet)
    {
        // This runtime function is always being called from wasm code.
        ClearThreadInWasmScope flag_scope;

        HandleScope scope(isolate);
        DCHECK_EQ(4, args.length());
        CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
        CONVERT_UINT32_ARG_CHECKED(table_index, 1);
        CONVERT_UINT32_ARG_CHECKED(entry_index, 2);
        CONVERT_ARG_CHECKED(Object, element_raw, 3);
        // TODO(mstarzinger): Manually box because parameters are not visited yet.
        Handle<Object> element(element_raw, isolate);
        DCHECK_LT(table_index, instance->tables()->length());
        auto table = handle(
            WasmTableObject::cast(instance->tables()->get(table_index)), isolate);

        if (!WasmTableObject::IsInBounds(isolate, table, entry_index)) {
            return ThrowWasmError(isolate, MessageTemplate::kWasmTrapTableOutOfBounds);
        }
        WasmTableObject::Set(isolate, table, entry_index, element);
        return ReadOnlyRoots(isolate).undefined_value();
    }

    RUNTIME_FUNCTION(Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(3, args.length());
        auto instance = Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
        CONVERT_UINT32_ARG_CHECKED(table_index, 0);
        CONVERT_UINT32_ARG_CHECKED(entry_index, 1);
        CONVERT_UINT32_ARG_CHECKED(sig_index, 2);
        DCHECK(isolate->context().is_null());
        isolate->set_context(instance->native_context());

        DCHECK_LT(table_index, instance->tables()->length());
        auto table_obj = handle(
            WasmTableObject::cast(instance->tables()->get(table_index)), isolate);

        // This check is already done in generated code.
        DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index));

        bool is_valid;
        bool is_null;
        MaybeHandle<WasmInstanceObject> maybe_target_instance;
        int function_index;
        WasmTableObject::GetFunctionTableEntry(
            isolate, table_obj, entry_index, &is_valid, &is_null,
            &maybe_target_instance, &function_index);

        CHECK(is_valid);
        if (is_null) {
            // We throw a signature mismatch trap to be in sync with the generated
            // code. There we do a signature check instead of a null-check. Trap
            // reasons are not defined in the spec. Otherwise, a null-check is
            // performed before a signature, according to the spec.
            return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch);
        }

        // Now we do the signature check.
        Handle<WasmInstanceObject> target_instance = maybe_target_instance.ToHandleChecked();

        const wasm::WasmModule* target_module = target_instance->module_object()->native_module()->module();

        wasm::FunctionSig* target_sig = target_module->functions[function_index].sig;

        auto target_sig_id = instance->module()->signature_map.Find(*target_sig);
        uint32_t expected_sig_id = instance->module()->signature_ids[sig_index];

        if (expected_sig_id != static_cast<uint32_t>(target_sig_id)) {
            return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch);
        }

        if (function_index < static_cast<int>(target_instance->module()->num_imported_functions)) {
            // The function in the target instance was imported. Use its imports table,
            // which contains a tuple needed by the import wrapper.
            ImportedFunctionEntry entry(target_instance, function_index);
            return entry.object_ref();
        }
        return *target_instance;
    }

    RUNTIME_FUNCTION(Runtime_WasmIndirectCallGetTargetAddress)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(2, args.length());
        auto instance = Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
        CONVERT_UINT32_ARG_CHECKED(table_index, 0);
        CONVERT_UINT32_ARG_CHECKED(entry_index, 1);

        DCHECK_LT(table_index, instance->tables()->length());
        auto table_obj = handle(
            WasmTableObject::cast(instance->tables()->get(table_index)), isolate);

        DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index));

        bool is_valid;
        bool is_null;
        MaybeHandle<WasmInstanceObject> maybe_target_instance;
        int function_index;
        WasmTableObject::GetFunctionTableEntry(
            isolate, table_obj, entry_index, &is_valid, &is_null,
            &maybe_target_instance, &function_index);

        CHECK(is_valid);
        // The null-check should already have been done in
        // Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance. That runtime
        // function should always be called first.
        CHECK(!is_null);

        Handle<WasmInstanceObject> target_instance = maybe_target_instance.ToHandleChecked();

        Address call_target = 0;
        if (function_index < static_cast<int>(target_instance->module()->num_imported_functions)) {
            // The function in the target instance was imported. Use its imports table,
            // which contains a tuple needed by the import wrapper.
            ImportedFunctionEntry entry(target_instance, function_index);
            call_target = entry.target();
        } else {
            // The function in the target instance was not imported.
            call_target = target_instance->GetCallTarget(function_index);
        }

        // The return value is an address and not a SMI. However, the address is
        // always aligned, and a SMI uses the same space as {Address}.
        CHECK(HAS_SMI_TAG(call_target));
        return Smi(call_target);
    }

    RUNTIME_FUNCTION(Runtime_WasmTableInit)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(5, args.length());
        auto instance = Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
        CONVERT_UINT32_ARG_CHECKED(table_index, 0);
        CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1);
        CONVERT_UINT32_ARG_CHECKED(dst, 2);
        CONVERT_UINT32_ARG_CHECKED(src, 3);
        CONVERT_UINT32_ARG_CHECKED(count, 4);

        DCHECK(isolate->context().is_null());
        isolate->set_context(instance->native_context());

        bool oob = !WasmInstanceObject::InitTableEntries(
            isolate, instance, table_index, elem_segment_index, dst, src, count);
        if (oob)
            return ThrowTableOutOfBounds(isolate, instance);
        return ReadOnlyRoots(isolate).undefined_value();
    }

    RUNTIME_FUNCTION(Runtime_WasmTableCopy)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(5, args.length());
        auto instance = Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
        CONVERT_UINT32_ARG_CHECKED(table_src_index, 0);
        CONVERT_UINT32_ARG_CHECKED(table_dst_index, 1);
        CONVERT_UINT32_ARG_CHECKED(dst, 2);
        CONVERT_UINT32_ARG_CHECKED(src, 3);
        CONVERT_UINT32_ARG_CHECKED(count, 4);

        bool oob = !WasmInstanceObject::CopyTableEntries(
            isolate, instance, table_src_index, table_dst_index, dst, src, count);
        if (oob)
            return ThrowTableOutOfBounds(isolate, instance);
        return ReadOnlyRoots(isolate).undefined_value();
    }
} // namespace internal
} // namespace v8
